package com.clementheliou.cheliou.dal.model.member.builder;

import static org.fest.util.Arrays.array;

import org.joda.time.LocalDate;
import org.springframework.util.Assert;

import com.clementheliou.cheliou.dal.dao.member.MemberDAO;
import com.clementheliou.cheliou.dal.model.member.Member;
import com.clementheliou.cheliou.dal.model.member.enumeration.MemberLocale;
import com.clementheliou.cheliou.dal.model.member.enumeration.MemberRole;
import com.clementheliou.cheliou.util.builder.ObjectBuilder;

/**
 * Builder used to get {@link Member} instances for test purpose. It must only be used through one
 * of its static factory methods.
 * 
 * @author Clément HELIOU (clement.heliou@gmail.com)
 * @see {@link Member};
 * @see {@link ObjectBuilder}.
 * @since 1.0
 */
public class MemberBuilder extends ObjectBuilder<Member> {

	/**
	 * The {@link MemberDAO} to be used to persist the built instance if needed.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private final MemberDAO memberDAO;

	/**
	 * Private default constructor.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	private MemberBuilder() {
		this(null);
	}

	/**
	 * Private constructor from the DAO to be used.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param memberDAO the {@link MemberDAO} to be used.
	 * @since 1.0
	 */
	private MemberBuilder(final MemberDAO memberDAO) {
		this.memberDAO = memberDAO;
	}

	/**
	 * Static factory method used to get a builder instance for a {@link Member} object that must be
	 * persistent. It's mostly used on data access layer's methods tests.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param memberDAO the {@link MemberDAO} to be used.
	 * @return the builder instance to be used.
	 * @since 1.0
	 */
	public static MemberBuilder with(final MemberDAO memberDAO) {
		return new MemberBuilder(memberDAO);
	}

	/**
	 * Static factory method used to get a builder instance for a {@link Member} object that must
	 * not be persistent. It's mostly used in tests not having a real data access.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance to be used.
	 * @since 1.0
	 */
	public static MemberBuilder withNoDependencies() {
		return new MemberBuilder();
	}

	/**
	 * Creates a default {@link Member} instance, ready to be persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance to be used.
	 * @since 1.0
	 */
	public MemberBuilder createDefaultMember() {
		setInstance(createInstance());
		return this;
	}

	/**
	 * Creates a default {@link Member} mock that should never be persisted.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public MemberBuilder createDefaultMemberMock() {
		return createDefaultMember();
	}

	/**
	 * Overrides the email of the default instance with the given value.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param email the value to be used.
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public MemberBuilder overridingEmail(final String email) {
		Assert.notNull(getObject(), "An instance must be created at this time");
		getObject().setEmail(email);
		return this;
	}

	/**
	 * Overrides the password of the default instance with the given value.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param email the value to be used.
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public MemberBuilder overridingPassword(final String password) {
		Assert.notNull(getObject(), "An instance must be created at this time");
		getObject().setPassword(password);
		return this;
	}

	/**
	 * Persists the built {@link Member} instance then flush the current session.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the builder instance after this action.
	 * @since 1.0
	 */
	public MemberBuilder persistAndFlush() {
		Assert.notNull(memberDAO, "memberDAO must not be null at this time");

		memberDAO.saveEntity(getObject());
		memberDAO.flushSession();
		return this;
	}

	/**
	 * Creates a {@link Member} instance with default values. These ones have a functional sense.
	 * The instance's hash code is added to the fields that need to be uniques.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @return the built instance.
	 * @see {@link Object#hashCode()}.
	 * @since 1.0
	 */
	private Member createInstance() {
		final Member defaultMember = new Member();

		defaultMember.setEmail("email@toto.fr" + defaultMember.hashCode());
		defaultMember.setFirstName("first");
		defaultMember.setLocale(MemberLocale.FRANCAIS);
		defaultMember.setName("name");
		defaultMember.setPassword("password");
		defaultMember.setRolesList(java.util.Arrays.asList(array(MemberRole.ADMINISTRATOR, MemberRole.USER)));
		defaultMember.setSubscriptionDate(new LocalDate());

		return defaultMember;
	}
}
